unit CoreVBO;

interface

uses
	SysUtils, gl, glext, CoreTypes, CoreMath;

const
	//vertType
	VBO_VERT = 1;
	VBO_NORM = 2;
	VBO_COORD = 4;

	//indexType
	VBO_BYTE = 0;
	VBO_WORD = 1;
	VBO_UINT = 2;

	//usage
	VBO_STATIC = 1;
	VBO_DYNAMIC = 2;
	VBO_STREAM = 4;

	VBO_DRAW = 8;
	VBO_READ = 16;
	VBO_COPY = 32;

	//prim
	VBO_TRIS = 0;

type
	TVBO = class(TListItem)
		verts, indices: TGLint;
		vertType, indexType: Integer;
		vertStride: Integer;
		usage: Integer;

		procedure Init(usage_, vertType_, indexType_: Integer; vertsCount: Integer; verts_: Pointer; indicesCount: Integer; indices_: Pointer);
		function Render(prim, start, count: Integer): Integer;

		constructor Create;
		destructor Destroy; override;
	end;

implementation

uses
	CoreConsole;

constructor TVBO.Create;
begin
	verts := 0;
	indices := 0;
end;

destructor TVBO.Destroy;
begin
	if indices <> 0 then glDeleteBuffersARB(1, @indices);
	if verts <> 0 then glDeleteBuffersARB(1, @verts);
end;

function VBOUsageToGL(usage: Integer): TGLuint;
begin
	if usage and VBO_DRAW <> 0 then
	begin
		if usage and VBO_STATIC <> 0 then Result := GL_STATIC_DRAW_ARB;
		if usage and VBO_DYNAMIC <> 0 then Result := GL_DYNAMIC_DRAW_ARB;
		if usage and VBO_STREAM <> 0 then Result := GL_STREAM_DRAW_ARB;
	end else

	if usage and VBO_READ <> 0 then
	begin
		if usage and VBO_STATIC <> 0 then Result := GL_STATIC_READ_ARB;
		if usage and VBO_DYNAMIC <> 0 then Result := GL_DYNAMIC_READ_ARB;
		if usage and VBO_STREAM <> 0 then Result := GL_STREAM_READ_ARB;
	end else

	if usage and VBO_COPY <> 0 then
	begin
		if usage and VBO_STATIC <> 0 then Result := GL_STATIC_COPY_ARB;
		if usage and VBO_DYNAMIC <> 0 then Result := GL_DYNAMIC_COPY_ARB;
		if usage and VBO_STREAM <> 0 then Result := GL_STREAM_COPY_ARB;
	end;
end;

function IndexToSize(indexType: Integer): Integer;
begin
	case indexType of
		VBO_BYTE: Result := 1;
		VBO_WORD: Result := 2;
		VBO_UINT: Result := 4;
	end;
end;

procedure TVBO.Init(usage_, vertType_, indexType_: Integer; vertsCount: Integer; verts_: Pointer; indicesCount: Integer; indices_: Pointer);
begin
	vertStride := 0;
	vertType := vertType_;
	indexType := indexType_;
	usage := usage_;

	if vertType and VBO_COORD <> 0 then vertStride += SizeOf(TVec2);
	if vertType and VBO_NORM <> 0 then vertStride += SizeOf(TVec3);
	if vertType and VBO_VERT <> 0 then vertStride += SizeOf(TVec3);

	glGenBuffersARB(1, @verts);
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, verts);
	glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertsCount * vertStride, verts_, VBOUsageToGL(usage));
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);

	glGenBuffersARB(1, @indices);
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indices);
	glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indicesCount * IndexToSize(indexType), indices_, VBOUsageToGL(usage));
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
end;

function IndexToGL(indexType: Integer): TGLuint;
begin
	case indexType of
		VBO_BYTE: Result := GL_UNSIGNED_BYTE;
		VBO_WORD: Result := GL_UNSIGNED_SHORT;
		VBO_UINT: Result := GL_UNSIGNED_INT;
	end;
end;

function PrimToGL(prim: Integer): TGLuint;
begin
	case prim of
		VBO_TRIS: Result := GL_TRIANGLES;
	end;
end;

function TVBO.Render(prim, start, count: Integer): Integer;
begin
	Result := count;

	glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);

	if vertType and VBO_VERT <> 0 then glEnableClientState(GL_VERTEX_ARRAY);
	if vertType and VBO_NORM <> 0 then glEnableClientState(GL_NORMAL_ARRAY);
	if vertType and VBO_COORD <> 0 then
	begin
		glClientActiveTextureARB(GL_TEXTURE0_ARB);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	end;

	glBindBufferARB(GL_ARRAY_BUFFER_ARB, verts);

	if vertType and VBO_COORD <> 0 then glTexCoordPointer(2, GL_FLOAT, vertStride, Pointer(0));
	if vertType and VBO_NORM <> 0 then glNormalPointer(GL_FLOAT, vertStride, Pointer(2 * SizeOf(Single)));
	if vertType and VBO_VERT <> 0 then glVertexPointer(3, GL_FLOAT, vertStride, Pointer(5 * Sizeof(Single)));

	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indices);
	glDrawElements(PrimToGL(prim), count, IndexToGL(indexType), nil);

	glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
	glPopClientAttrib;
end;

end.
